package com.gitee.wsl.mathematics.interpolator.ext

import com.gitee.wsl.math.Math
import com.gitee.wsl.mathematics.interpolator.PercentOrRatio
import com.gitee.wsl.mathematics.interpolator.Ratio
import com.gitee.wsl.unit.angle.AngleUnit
import com.gitee.wsl.unit.angle.ext.minus
import com.gitee.wsl.unit.angle.ext.plus

val AngleUnit.Companion.EPSILON: AngleUnit
    get() = AngleUnit.fromRatio(0.00001)
/*val AngleUnit.Companion.ZERO: AngleUnit
    get() = AngleUnit.fromRatio(0.0)*/
val AngleUnit.Companion.QUARTER: AngleUnit
    get() = AngleUnit.fromRatio(0.25)
val AngleUnit.Companion.HALF: AngleUnit
    get() = AngleUnit.fromRatio(0.5)
val AngleUnit.Companion.THREE_QUARTERS: AngleUnit
    get() = AngleUnit.fromRatio(0.75)
val AngleUnit.Companion.FULL: AngleUnit
    get() = AngleUnit.fromRatio(1.0)

/** [0..PI * 2] radians -> [0..360] degrees */
val AngleUnit.ratio: Ratio get() = AngleUnit.radiansToRatio(radian)

inline fun AngleUnit.Companion.fromRatio(ratio: Float): AngleUnit = AngleUnit.Radians(ratioToRadians(ratio.toRatio()))
inline fun AngleUnit.Companion.fromRatio(ratio: Double): AngleUnit = AngleUnit.Radians(ratioToRadians(ratio.toRatio()))
inline fun AngleUnit.Companion.fromRatio(ratio: PercentOrRatio): AngleUnit = AngleUnit.Radians(ratioToRadians(ratio))

inline fun AngleUnit.Companion.ratioToDegrees(ratio: PercentOrRatio): Double = ratio * 360.0
inline fun AngleUnit.Companion.ratioToRadians(ratio: PercentOrRatio): Double = ratio * Math.TWO_PI

inline fun AngleUnit.Companion.degreesToRatio(degrees: Double): Ratio = Ratio(degrees / 360.0)
inline fun AngleUnit.Companion.radiansToRatio(radians: Double): Ratio = Ratio(radians / Math.TWO_PI)

fun Ratio.interpolateAngle(l: AngleUnit, r: AngleUnit, minimizeAngle: Boolean): AngleUnit = _interpolateAngleAny(this, l, r, minimizeAngle)
fun Ratio.interpolateAngle(l: AngleUnit, r: AngleUnit): AngleUnit = interpolateAngle(l, r, minimizeAngle = true)
fun Ratio.interpolateAngleNormalized(l: AngleUnit, r: AngleUnit): AngleUnit = interpolateAngle(l, r, minimizeAngle = true)
fun Ratio.interpolateAngleDenormalized(l: AngleUnit, r: AngleUnit): AngleUnit = interpolateAngle(l, r, minimizeAngle = false)

private fun _interpolateAngleAny(ratio: Ratio, l: AngleUnit, r: AngleUnit, minimizeAngle: Boolean = true): AngleUnit {
    if (!minimizeAngle) return AngleUnit.fromRatio(ratio.interpolate(l.ratio, r.ratio))
    val ln = l.normalized
    val rn = r.normalized
    return when {
        (rn - ln).absoluteValue <= AngleUnit.HALF -> AngleUnit.fromRadians(ratio.interpolate(ln.radian, rn.radian))
        ln < rn -> AngleUnit.fromRadians(ratio.interpolate((ln + AngleUnit.FULL).radian, rn.radian)).normalized
        else -> AngleUnit.fromRadians(ratio.interpolate(ln.radian, (rn + AngleUnit.FULL).radian)).normalized
    }
}